<?php
/**
 * @link      http://www.phpGrace.com
 * @copyright Copyright (c) 2010-2020 phpGrace.com
 * @license   http://www.phpGrace.com/license
 * @package   phpGrace
 * @author    haijun liu mail:5213606@qq.com
 * @version   2.0
 *
 * 2022.11.01: * 去掉类GraceModel  model函数等
 *
 */
/* 行错误及异常处理  */
error_reporting(E_ALL);
ini_set('display_errors', 'off');
// 内存及运行时间起始记录
define('PG_START_MEMORY', memory_get_usage());
define('PG_START_TIME', microtime(true));
// 站点首页配置
define('PG_INDEX_FILE_NAME', 'index.php');
// 页面后缀
if (!defined('PG_SUFFIX')) {define('PG_SUFFIX', false);}
// 框架版本
define('PG_VERSION', '2.0');
// 系统分隔符
define('PG_DS', DIRECTORY_SEPARATOR);
// 框架核心目录所在位置
define('PG_IN', dirname(__FILE__) . PG_DS);
// 视图文件路径形式
// file : 文件形式     例 : 视图文件夹/控制器_方法.php
// dir  : 文件夹形式 例 : 视图文件夹/控制器/方法.php
if (!defined('PG_VIEW_TYPE')) {define('PG_VIEW_TYPE', 'file');}
// 是否打开调试模式
if (!defined('PG_DEBUG')) {define('PG_DEBUG', false);}
// 是否展示错误信息 [ 默认隐藏所有错误,运行报错服务器状态 500 ]
if (!defined('PG_SHOWERROR')) {define('PG_SHOWERROR', false);}
// 是否自动展示视图, 如果项目为api接口不需要视图可以设置为 false
if (!defined('PG_AUTO_DISPLAY')) {define('PG_AUTO_DISPLAY', true);}
// 是否开启自定义路由
if (!defined('PG_ROUTE')) {define('PG_ROUTE', false);}
// 全局关闭缓存 [ 调试时可以开启此项来观察数据变化 ]
if (!defined('PG_CLOSE_CACHE')) {define('PG_CLOSE_CACHE', false);}
// 文件型 sessions 文件存放路径
if (!defined('PG_SESSION_DIR')) {define('PG_SESSION_DIR', './_runtime/sessions');}
// session 存储类型  [file, memcache, redis]
if (!defined('PG_SESSION_TYPE')) {define('PG_SESSION_TYPE', 'file');}
// 是否全应用启动 session
if (!defined('PG_SESSION_START')) {define('PG_SESSION_START', false);}
//session 类似为 memcache 或 redis 时，对应的主机地址 [memcache 11211 redis 6379]
if (!defined('PG_SESSION_HOST')) {define('PG_SESSION_HOST', 'tcp://127.0.0.1:11211');}
// 应用所在目录
if (!defined('PG_PATH')) {define('PG_PATH', './app');}
// 控制器文件所在目录
define('PG_CONTROLLER', 'controllers');
// 视图文件所在目录
define('PG_VIEW', 'views');
// 工具类文件所在目录
define('PG_TOOLS', 'tools');
// 语言包文件所在目录
define('PG_LANG_PACKAGE', 'lang');
// 全局配置文件名称
define('PG_CONF', 'config.php');
// 文件型缓存数据文件保存目录
define('PG_CACHEDATAFILES', PG_PATH . '_runtime/cacheDataFiles');
// 判断是否是ajax请求
define('IS_AJAX', (isset($_SERVER["HTTP_X_REQUESTED_WITH"]) && strtolower($_SERVER["HTTP_X_REQUESTED_WITH"]) == 'xmlhttprequest') ? 'ajax' : 'noajax');

// 基础控制器定义
class grace
{
    // url 解析后获得的数据
    public $gets;
    // 核心数据表名
    public $tableName = null;
    // 数据表主键
    public $tableKey = null;
    // 数据表操作对象
    public $db;
    // 数据排序规则
    public $order = null;
    // 是否过滤 $_POST 数据内的 < > , 可防止跨站攻击
    public $postFilter = true;
    // 网页信息 array(页面标题, 页面关键字, 页面描述)
    public $pageInfo = array('', '', '');
    // 缓存对象
    protected $cacher = null;
    // 缓存名称
    protected $cacheName;
    // 是否运行追踪
    public    $trace      = true;

    // 视图基础目录
    protected $templateDir;
    // 构造函数
    public function __construct()
    {}
    // 初始化函数
    public function __init()
    {
        $this->templateDir = PG_PATH . '/' . PG_VIEW . '/';
        if ($this->tableName != null) {$this->db = db($this->tableName);}
        // 过滤 $_POST
        if (!empty($_POST)) {
            define('PG_POST', true);
            if ($this->postFilter) {$_POST = str_replace(array('<', '>', '"', "'"), array('&lt;', '&gt;', '&quot;', ''), $_POST);}
        } else {
            define('PG_POST', false);
        }
        // 过滤 $_GET
        if (!empty($_GET)) {$_GET = str_replace(array('<', '>', '"', "'"), array('&lt;', '&gt;', '&quot;', ''), $_GET);}
        if (!empty($this->gets)) {$this->gets = str_replace(array('<', '>', '"', "'"), array('&lt;', '&gt;', '&quot;', ''), $this->gets);}
    }
    // 默认 index
    public function index()
    {}
    // 视图展示
    public function display($tplName = null)
    {
        if (PG_VIEW_TYPE == 'file') {
            $tplUrl = is_null($tplName) ? $this->templateDir . PG_C . '_' . PG_M . '.php' : $this->templateDir . $tplName;
        } else {
            $tplUrl = is_null($tplName) ? $this->templateDir . PG_C . '/' . PG_M . '.php' : $this->templateDir . $tplName;
        }
        if (is_file($tplUrl)) {include $tplUrl;}
    }
    // 语言包设置
    protected function setLang($langType)
    {
        pgSetCookie('phpGraceLang', $langType);
    }

    // 获取缓存对象
    protected function getCacher()
    {
        if (!empty($this->cacher)) {return null;}
        $config = c('cache');
        if (empty($config)) {throw new graceException('缓存设置错误');}
        if (!in_array($config['type'], c('allowCacheType'))) {throw new graceException('缓存类型错误');}
        $type         = strtolower($config['type']);
        $className    = 'phpGrace\\tools\\caches\\' . $type . 'Cacher';
        $this->cacher = $className::getInstance($config);
    }
    // 进行缓存工作
    protected function cache($name, $id = null, $queryMethod, $timer = 3600, $isSuper = true)
    {
        if (PG_CLOSE_CACHE) {
            $queryRes    = $this->$queryMethod();
            $this->$name = $queryRes;
            return false;
        }
        $this->getCacher();
        $this->cacheName = \graceCacheName($name, $id, $isSuper);
        $cachedRes       = $this->cacher->get($this->cacheName);
        if ($cachedRes) {$this->$name = $cachedRes;return true;}
        $queryRes = $this->$queryMethod();
        $this->cacher->set($this->cacheName, $queryRes, $timer);
        $this->$name = $queryRes;
    }
    // 清除全部缓存
    public function clearCache()
    {
        $this->getCacher();
        $this->cacher->clearCache();
    }
    // 清除指定缓存
    public function removeCache($name, $id = null, $isSuper = true)
    {
        $this->getCacher();
        $name = \graceCacheName($name, $id, $isSuper);
        $this->cacher->removeCache($name);
    }
    // 初始化 gets 数据
    // 如果某个指定的数据为空则进行定义及赋值
    protected function initGets($key, $val = '')
    {
        if (empty($this->gets[$key])) {$this->gets[$key] = $val;}
    }
    // 将 gets 指定数据整数化
    protected function intGets($key, $val = 0)
    {
        $this->gets[$key] = empty($this->gets[$key]) ? $val : intval($this->gets[$key]);
    }
}

/* 缓存基础类 */
class cacheBase
{
    // 缓存对象
    protected $cacher = null;
    // 缓存参数
    protected $parameters;
    // 构造函数
    public function __construct()
    {
        $config = c('cache');
        if (empty($config)) {throw new graceException('缓存设置错误');}
        if (!in_array($config['type'], c('allowCacheType'))) {throw new graceException('缓存类型错误');}
        $type         = strtolower($config['type']);
        $className    = 'phpGrace\\tools\\caches\\' . $type . 'Cacher';
        $this->cacher = $className::getInstance($config);
    }
    // 设置并获取缓存数据
    public function cache($name, $parameter = null, $queryMethod, $timer = 3600, $isSuper = true)
    {
        $this->parameters = $parameter;
        if (PG_CLOSE_CACHE) {return $this->$queryMethod();}
        $name      = graceCacheName($name, $parameter, $isSuper);
        $cachedRes = $this->cacher->get($name);
        if ($cachedRes) {return $cachedRes;}
        $queryRes = $this->$queryMethod();
        if (empty($queryRes)) {return $queryRes;}
        $this->cacher->set($name, $queryRes, $timer);
        return $queryRes;
    }
    // 删除缓存
    public function removeCache($name, $parameter = null, $isSuper = true)
    {
        $name = graceCacheName($name, $parameter, $isSuper);
        $this->cacher->removeCache($name);
    }
}
/* 框架内置函数 */
function graceErrorHandler($code, $message, $file, $line)
{
    if (!PG_SHOWERROR) {return false;}
    define('systemErrors', json_encode(array($code, $message, $file, $line)));
    include PG_IN . 'templates' . PG_DS . 'error.php';
    exit;
}
set_error_handler("graceErrorHandler");
register_shutdown_function(function () {
    if (!PG_SHOWERROR) {return false;}
    $error = error_get_last();
    if (!empty($error)) {
        define('systemErrors', json_encode(array('0', $error['message'], $error['file'], $error['line'])));
        include (PG_IN . 'templates' . PG_DS . 'error.php');
        exit;
    }
});
class graceException extends Exception
{public function showBug()
    {if (PG_DEBUG) {include PG_IN . 'templates' . PG_DS . 'debug.php';}}}

/**
 * 框架类自动加载
 * @param string $className 类名称
 */
function __graceAutoLoad($className)
{
    // 自定义控制器文件加载
    if (substr($className, -10) == 'Controller') {
        $fileUri = PG_PATH . '/' . PG_CONTROLLER . '/' . substr($className, 0, -10) . '.php';
        if (is_file($fileUri)) {require $fileUri;}
    }
    // 利用命名空间加载其它类文件
    else {
        $fileUri = PG_IN . substr($className, 9) . '.php';
        if (PG_DS == '/') {$fileUri = str_replace('\\', '/', $fileUri);}
        if (is_file($fileUri)) {require $fileUri;}
    }
}
spl_autoload_register('__graceAutoLoad');

/**
 *  如果调试开启 则 运行追踪(右侧)
 */
function gracesTrace(){
    if(!PG_DEBUG){return false;}
    include PG_IN.'templates'.PG_DS.'trace.php';
}

/**
 * 终止程序运行并输出一段消息
 * @param mixed $msg 文本类型的消息内容
 */
function pgExit($msg = '')
{exit($msg);}

/**
 * 功能 : 获取一个数据表操作对象
 * @param  string $tableName  数据表名称
 * @param  string $configName 默认 db , 对应的数据库一级2配置名称
 * @return object             数据库操作对象
 */
function db($tableName, $configName = 'db')
{
    $conf = c($configName);
    return phpGrace\tools\db::getInstance($conf, $tableName, $configName);
}



/**
 * 功能 : 工具实例化函数( 适用于不能使用命名空间的工具类 )
 * @param  mixed $args 动态参数
 * @return object      对应的工具对象
 */
function tool($args)
{
    static $staticTools = array();
    $arguments          = func_get_args();
    $className          = array_shift($arguments);
    $className          = '\\' . $className;
    if (empty($staticTools[$className])) {
        $fileUri = PG_IN . PG_TOOLS . PG_DS . $className . '.php';
        if (!is_file($fileUri)) {throw new graceException("类文件 {$className} 不存在");}
        include $fileUri;
        $staticTools[$className] = 1;
    }
    switch (count($arguments)) {
        case 0:
            return new $className();
            break;
        case 1:
            return new $className($arguments[0]);
            break;
        case 2:
            return new $className($arguments[0], $arguments[1]);
            break;
        case 3:
            return new $className($arguments[0], $arguments[1], $arguments[2]);
            break;
        case 4:
            return new $className($arguments[0], $arguments[1], $arguments[2], $arguments[3]);
            break;
        case 5:
            return new $className($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]);
            break;
    }
}
/**
 * 路由解析
 * @return array
 */
function graceRouter()
{
    if (isset($_GET['pathInfo'])) {$path = $_GET['pathInfo'];unset($_GET['pathInfo']);} else { $path = 'index/index';}
    if (PG_SUFFIX) {$path = str_replace(PG_SUFFIX, '', $path);}
    $router = explode('/', $path);
    if (empty($router[0])) {array_shift($router);}
    if (PG_ROUTE) {
        $routerArray = require PG_PATH . '/router.php';
        if (array_key_exists($router[0], $routerArray)) {
            $newRouter    = array();
            $newRouter[0] = $routerArray[$router[0]][0];
            $newRouter[1] = $routerArray[$router[0]][1];
            if (!empty($routerArray[$router[0]][2]) && is_array($routerArray[$router[0]][2])) {
                $newRouter = array_merge($newRouter, $routerArray[$router[0]][2]);
            }
            define("PG_PAGE", 1);
            return $newRouter;
        }
        ;
    }
    $router[0] = isset($router[0]) ? $router[0] : 'index';
    $router[1] = isset($router[1]) ? $router[1] : 'index';
    for ($i = 2; $i < count($router); $i++) {
        if (preg_match('/^page_(.*)(' . PG_SUFFIX . ')*$/Ui', $router[$i], $matches)) {
            define("PG_PAGE", intval($matches[1]));
            array_splice($router, $i, 1);
        }
    }
    if (!defined("PG_PAGE")) {define("PG_PAGE", 1);}
    return $router;
}

/**
 * 当前分组内的自定义配置 [可按照格式进行自定义配置]
 * @param string key1 配置名称1
 * @param string key2 配置名称2
 * @return mixed      对应配置值
 */
function c($key1, $key2 = null)
{
    static $config = null;
    if ($config == null) {$config = require PG_PATH . '/config.php';}
    if (is_null($key1)) {return $config;}
    if (is_null($key2)) {if (isset($config[$key1])) {return $config[$key1];}
        return null;}
    if (isset($config[$key1][$key2])) {return $config[$key1][$key2];}
    return null;
}
/**
 * 全局配置 [可按照格式进行自定义配置]
 * @param  string $key1 配置名称1
 * @param  string $key2 配置名称2
 * @return mixed        对应的配置
 */
function sc($key1 = null, $key2 = null)
{
    static $config = null;
    if ($config == null) {
        $config = require PG_IN . 'config.php';
    }
    if (is_null($key1)) {return $config;}
    if (is_null($key2)) {if (isset($config[$key1])) {return $config[$key1];}
        return null;}
    if (isset($config[$key1][$key2])) {return $config[$key1][$key2];}
    return null;
}
/**
 * 时间、内存开销计算
 * @return array(耗时[毫秒], 消耗内存[K])
 */
function pgCost()
{
    return array(
        round((microtime(true) - PG_START_TIME) * 1000, 2),
        round((memory_get_usage() - PG_START_MEMORY) / 1024, 2),
    );
}
/**
 * 开启 session
 * @return void
 */
function startSession()
{
    switch (PG_SESSION_TYPE) {
        case 'file':
            if (!is_dir(PG_SESSION_DIR)) {mkdir(PG_SESSION_DIR, 0777, true);}
            session_save_path(PG_SESSION_DIR);
            break;
        case 'memcache':
            ini_set("session.save_handler", "memcache");
            ini_set("session.save_path", PG_SESSION_HOST);
            break;
        case 'redis':
            ini_set("session.save_handler", "redis");
            ini_set("session.save_path", PG_SESSION_HOST);
            break;
        default:
            if (!is_dir(PG_SESSION_DIR)) {mkdir(PG_SESSION_DIR, 0777, true);}
            session_save_path(PG_SESSION_DIR);
    }
    session_start();
    session_write_close();
}

/**
 * 设置 cookie
 * @param  string $name   cookie 名称
 * @param  mixed $val     对应的值
 * @param  int   $expire  有效时间
 * @return void
 */
function pgSetCookie($name, $val, $expire = 31536000)
{
    $expire += time();
    @setcookie($name, $val, $expire, '/');
    $_COOKIE[$name] = $val;
}
/**
 * 获取 cookie
 * @param string $name cookie 名称
 * @return string 具体 cookie 值或 null
 */
function pgGetCookie($name)
{if (isset($_COOKIE[$name])) {return $_COOKIE[$name];}
    return null;}
/**
 * 删除指定 cookie
 * @param  string $name cookie 名称
 * @return void
 */
function pgRemoveCookie($name)
{setcookie($name, 'null', time() - 1000, '/');}

/**
 * 获取语言
 * @param  string $key 语言包键名称
 * @return string 具体的值或者null
 * @throws graceException
 */
function lang($key)
{
    static $Lang = null;
    if (is_null($Lang)) {
        $langName = empty($_COOKIE['phpGraceLang']) ? 'zh' : $_COOKIE['phpGraceLang'];
        $langFile = PG_PATH . '/' . PG_LANG_PACKAGE . '/' . $langName . '.php';
        if (is_file($langFile)) {
            $Lang = require $langFile;
        } else {
            throw new graceException('语言包文件不存在');
        }
    }
    if (isset($Lang[$key])) {return $Lang[$key];}
    return null;
}

/**
 * 生成一个 token [ cookie 模式 ]
 * @return string 具体的值或者null
 */
function setToken()
{
    $token = uniqid();
    pgSetCookie('__gracetoken__', $token);
    return $token;
}
/**
 * 获取 token [ cookie 模式 ]，并销毁
 * @return string 具体的值或者null
 */
function getToken()
{
    $token = pgGetCookie('__gracetoken__');
    pgRemoveCookie('__gracetoken__');
    return $token;
}

/**
 * 规划缓存命名
 * @param  string  $name      缓存名称
 * @param  mixed   $parameter 缓存影响参数
 * @param  boolean $isSuper   是否为全局缓存
 * @return string 缓存名称
 */
function graceCacheName($name, $parameter = '', $isSuper = true)
{
    $cacheConfig = c('cache');
    $parameter   = is_array($parameter) ? implode('_', $parameter) : $parameter;
    $cacheName   = $isSuper ? $cacheConfig['pre'] . $name . $parameter : $cacheConfig['pre'] . PG_C . '_' . PG_M . '_' . $name . $parameter;
    if (empty($cacheConfig['name2md5'])) {return $cacheName;}
    return md5($cacheName);
}
/**
 * 修正POST参数
 * @param string $name    $_POST 键名称
 * @param mixed  $value   默认值
 * @return mixed 修正后的值
 */
function initPOST($name, $value = '')
{if (empty($_POST[$name])) {$_POST[$name] = $value;}
    return $_POST[$name];}
/**
 * 修正 GET 参数
 * @param string $name    $_GET 键名称
 * @param mixed $value   默认值
 * @return mixed 修正后的值
 */
function initGET($name, $value = '')
{if (empty($_GET[$name])) {$_GET[$name] = $value;}
    return $_GET[$name];}
require_once 'functions.php'; //载入功能增强函数
// 框架启动
try {
    $includedFiles = get_included_files();
    if (count($includedFiles) < 3) {exit;}
    header('content-type:text/html; charset=utf-8');
    if (PG_SESSION_START) {startSession();}
    if (!is_dir(PG_PATH)) {
        include PG_IN . 'graceCreate.php';
        graceCreateApp();}
    $router         = graceRouter();
    $controllerName = $router[0];
    $mode           = '/^([a-z]|[A-Z]|[0-9])+$/Uis';
    $res            = preg_match($mode, $controllerName);
    if (!$res) {$controllerName = 'index';}
    $controllerFile = PG_PATH . '/' . PG_CONTROLLER . '/' . $controllerName . '.php';
    if (!is_file($controllerFile)) {
        $controllerName = 'index';
        $controllerFile = PG_PATH . '/' . PG_CONTROLLER . '/index.php';
    }
    require $controllerFile;
    define('PG_C', $controllerName);
    $controllerName = $controllerName . 'Controller';
    $controller     = new $controllerName;
    if (!$controller instanceof grace) {throw new graceException('[ ' . $controllerName . ' ] 必须继承自 grace');}
    $methodName = $router[1];
    $res        = preg_match($mode, $methodName);
    if (!$res) {$methodName = 'index';}
    $graceMethods = array(
        '__init', 'display', 'json', 'dataList', 'getDataById', 'getDefaultVal',
        'skipToIndex', 'getCacher', 'cache', 'clearCache', 'removeCache', 'initVal', 'intVal',
    );
    if (in_array($methodName, $graceMethods)) {$methodName = 'index';}
    if (!method_exists($controller, $methodName)) {$methodName = 'index';}
    define('PG_M', $methodName);
    define('PG_SROOT', str_replace(PG_INDEX_FILE_NAME, '', $_SERVER['PHP_SELF']));
    array_shift($router);
    array_shift($router);
    $controller->gets = $router;
    define('PG_URL', implode('/', $router));
    call_user_func(array($controller, '__init'));
    $GLOBALS['graceSql'] = array();
    call_user_func(array($controller, $methodName));
    if (PG_AUTO_DISPLAY) {call_user_func(array($controller, 'display'));}
    if($controller->trace){gracesTrace();}
} catch (graceException $e) {$e->showBug();}
